package com.chenfan.mcn.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.common.exception.BusinessException;
import com.chenfan.common.vo.UserVO;
import com.chenfan.mcn.clients.baseinfo.*;
import com.chenfan.mcn.clients.finance.FinanceClient;
import com.chenfan.mcn.clients.privilege.PrivilegeClient;
import com.chenfan.mcn.clients.privilege.ScompanyRes;
import com.chenfan.mcn.config.UserVoContextHolder;
import com.chenfan.mcn.constant.BillCodeCst;
import com.chenfan.mcn.constant.NumberCst;
import com.chenfan.mcn.constant.SeparatorCst;
import com.chenfan.mcn.enums.*;
import com.chenfan.mcn.dao.*;
import com.chenfan.mcn.exception.McnErrorCode;
import com.chenfan.mcn.extension.Res;
import com.chenfan.mcn.component.CodeGenerateHelper;
import com.chenfan.mcn.model.*;
import com.chenfan.mcn.model.dto.AnnualRebateApprovalAddDTO;
import com.chenfan.mcn.model.dto.AnnualRebateApprovalSearchDTO;
import com.chenfan.mcn.model.dto.ApprovalFlowDTO;
import com.chenfan.mcn.model.rpc.ChangeAnnualRebateStatusCallback;
import com.chenfan.mcn.model.vo.*;
import com.chenfan.mcn.mq.producer.AnnualRebateApprovalMessageProducer;
import com.chenfan.mcn.service.*;
import com.chenfan.mcn.extension.excel.ExcelUtils;
import com.chenfan.mcn.utils.BusinessAssert;
import com.chenfan.mcn.utils.CommonUtil;
import com.chenfan.mcn.utils.RpcUtil;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 客户返点审批单 服务实现类
 * </p>
 *
 * @author lizhejin
 * @since 2021-03-02
 */
@Slf4j
@Service
public class AnnualRebateApprovalServiceImpl implements AnnualRebateApprovalService, ApprovalCallbackService {

    @Autowired
    private AnnualRebateApprovalMapper annualRebateApprovalMapper;

    @Autowired
    private ApprovalFlowService approvalFlowService;

    @Autowired
    private BaseInfoClient baseInfoClient;

    @Autowired
    private YearFrameContractMapper yearFrameContractMapper;

    @Autowired
    private DiscountApprovalMapper discountApprovalMapper;

    @Autowired
    private IncomeContractMapper incomeContractMapper;

    @Autowired
    private PlatformDiscountRangeService platformDiscountRangeService;

    @Autowired
    private FinanceClient financeClient;

    @Autowired
    private AnnualRebateApprovalMessageProducer annualRebateApprovalProducer;

    @Autowired
    private PlatformDiscountRangeMapper platformDiscountRangeMapper;

    @Autowired
    private MessageInfoService messageInfoService;

    @Autowired
    private BillAssociationDetailMapper billAssociationDetailMapper;

    @Autowired
    private ContractExcutionOrderMapper contractExcutionOrderMapper;

    @Autowired
    private BrandInfoService brandInfoService;

    @Autowired
    private PrivilegeClient privilegeClient;

    /**
     * 客户返点审批单列表
     *
     * @param page      {@link McnPage}
     * @param searchDTO {@link AnnualRebateApprovalSearchDTO}
     * @return {@link Page}
     */
    @Override
    public Page<AnnualRebateVO> getList(McnPage<AnnualRebateVO> page, AnnualRebateApprovalSearchDTO searchDTO) {
        searchDTO.setProcessId(ApprovalEnum.ANNUAL_REBATE_APPROVAL.getProcessId());
        searchDTO.setAlias("ara.");
        if (Objects.nonNull(searchDTO.getFlowUserId())) {
            searchDTO.setSrcIds(approvalFlowService.getSrcList(Lists.newArrayList(searchDTO.getFlowUserId()), searchDTO.getProcessId()));
        }
        Page<AnnualRebateVO> list = annualRebateApprovalMapper.getList(page, searchDTO);
        List<Long> customerIdList = list.getRecords().stream().map(AnnualRebateVO::getCustomerId).distinct().collect(Collectors.toList());
        Map<Long, BaseCustomer> customerMapping = CommonUtil.getIfNecessary(CollectionUtils.isNotEmpty(customerIdList),
                () -> Objects.requireNonNull(baseInfoClient.getMcnCustomerByIds(customerIdList)).getObj(), Collections.EMPTY_MAP);
        List<Long> flowList = list.getRecords().stream().map(AnnualRebateVO::getFlowId).filter(Objects::nonNull).collect(Collectors.toList());
        JSONObject flowMapping = null;
        if (flowList.size() > 0) {
            flowMapping = approvalFlowService.getFlowStatus(flowList);
        }
        if (Objects.nonNull(flowMapping) || Objects.nonNull(customerMapping)) {
            JSONObject finalFlowMapping = flowMapping;
            list.getRecords().forEach(vo -> {
                if (customerMapping.size() > 0) {
                    BaseCustomer customer = customerMapping.get(vo.getCustomerId());
                    vo.setCustomerName(Objects.nonNull(customer) ? customer.getCustomerNameC() : "");
                }
                if (Objects.nonNull(finalFlowMapping) && finalFlowMapping.size() > 0 && Objects.nonNull(vo.getFlowId())) {
                    vo.setFlowStatusName(finalFlowMapping.getString(vo.getFlowId().toString()));
                }
            });
        }
        return list;
    }

    /**
     * 修改客户返点审批单状态
     *
     * @param id     {@link Long}
     * @param status {@link Integer}
     * @return {@link Res}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changeStatus(Long id, Integer status) {
        AnnualRebateApproval annualRebateApproval = annualRebateApprovalMapper.selectById(id);
        if (Objects.isNull(annualRebateApproval)) {
            throw new BusinessException(McnErrorCode.ANNUAL_NOT_EXISTS);
        }
        validateStatus(annualRebateApproval, status);
        if (status == AnnualRebateApprovalEnum.APPROVAL.code()) {
            BusinessAssert.notNull(annualRebateApproval.getContractingId(), "我司主体不能为空");
            approvalFlowService.startProcess(id, annualRebateApproval.getRebateContractCode(), ApprovalEnum.ANNUAL_REBATE_APPROVAL, JSON.toJSONString(annualRebateApproval), jsonString(annualRebateApproval));
        }
        if (status == AnnualRebateApprovalEnum.CANCEL.code()) {
            approvalFlowService.revokeProcess(id, ApprovalEnum.ANNUAL_REBATE_APPROVAL.getProcessId());
        } else if (status == AnnualRebateApprovalEnum.INVALID.code()) {
            if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.WAITING_PAYMENT.code()) {
                //判断该单据是否能在财务模块费用表查询到非删除状态的费用
                Integer obj = Objects.requireNonNull(
                        financeClient.getInvalid(annualRebateApproval.getRebateContractCode(),annualRebateApproval.getRebateContractCode(), null)).getObj();
                if (obj > 0) {
                    throw new BusinessException(McnErrorCode.ANNUAL_INVALID_FAIL);
                }
            }
            approvalFlowService.revokeProcess(id, ApprovalEnum.ANNUAL_REBATE_APPROVAL.getProcessId());
            //关联执行单
            List<BillAssociationDetail> associationList = billAssociationDetailMapper.selectList(Wrappers.lambdaQuery(BillAssociationDetail.class)
                    .eq(BillAssociationDetail::getBillId, id) .eq(BillAssociationDetail::getBillType, NumberCst.ZERO)
                    .eq(BillAssociationDetail::getIsInvalid, NumberCst.ZERO).eq(BillAssociationDetail::getIsDelete, NumberCst.ZERO));
            if(CollectionUtils.isNotEmpty(associationList)) {
                List<Long> excutionOrderIds = CommonUtil.defaultList(associationList).stream().map(BillAssociationDetail::getExcutionOrderId).distinct().collect(Collectors.toList());
                //作废关联关系
                billAssociationDetailMapper.updateByNoStrategy(Wrappers.lambdaUpdate(BillAssociationDetail.class)
                        .set(BillAssociationDetail::getIsInvalid, NumberCst.ONE)
                        .set(BillAssociationDetail::getUpdateBy, UserVoContextHolder.getUserId())
                        .set(BillAssociationDetail::getUpdateName, UserVoContextHolder.getUserName())
                        .set(BillAssociationDetail::getUpdateDate, new Date())
                        .eq(BillAssociationDetail::getBillId, id)
                        .eq(BillAssociationDetail::getBillType, NumberCst.ZERO)
                );
                //执行单-客户返点结算状态置为未结算
                contractExcutionOrderMapper.updateByNoStrategy(Wrappers.<ContractExcutionOrder>lambdaUpdate()
                        .set(ContractExcutionOrder::getRebateSettleStatus, NumberCst.ZERO)
                        .set(ContractExcutionOrder::getUpdateBy, UserVoContextHolder.getUserId())
                        .set(ContractExcutionOrder::getUpdateName, UserVoContextHolder.getUserName())
                        .set(ContractExcutionOrder::getUpdateDate, new Date())
                        .in(ContractExcutionOrder::getId, excutionOrderIds));
            }

        }
        annualRebateApproval.setApprovalStatus(status);
        return this.updateById(annualRebateApproval);
    }


    public String jsonString(AnnualRebateApproval annualRebateApproval) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("confirmRebateAmount", annualRebateApproval.getConfirmRebateAmount());
        return jsonObject.toJSONString();
    }

    /**
     * 新增/编辑客户返点审批单
     *
     * @param addDTO {@link AnnualRebateApprovalAddDTO}
     * @return {@link Res}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveData(AnnualRebateApprovalAddDTO addDTO) {
        AnnualRebateApproval annualRebateApproval = new AnnualRebateApproval();
        BeanUtils.copyProperties(addDTO, annualRebateApproval);
        if(CollectionUtils.isNotEmpty(addDTO.getExcuteIds())) {
            List<Long> contractingIds = CommonUtil.defaultList(contractExcutionOrderMapper.getContractingIds(addDTO.getExcuteIds()));
            BusinessAssert.isTrue(contractingIds.size() <= 1, "关联执行单间存在不同的「我司主体」");
            BusinessAssert.isTrue(contractingIds.contains(addDTO.getContractingId()), "该单据与关联执行单-「我司主体」不一致！");
        }
        List<Long> insertExcutionOrderIds = null;
        if (Objects.isNull(annualRebateApproval.getId())) {
            annualRebateApproval.setRebateContractCode(CodeGenerateHelper.getCode(BillCodeCst.ANNUAL_REBATE_APPROVAL));
            annualRebateApprovalMapper.insert(annualRebateApproval);
            insertExcutionOrderIds = addDTO.getExcuteIds();
        } else {
            AnnualRebateApproval entity = annualRebateApprovalMapper.selectById(annualRebateApproval.getId());
            BusinessAssert.notNull(entity, McnErrorCode.ANNUAL_NOT_EXISTS);
            BusinessAssert.isTrue(AnnualRebateApprovalEnum.anyMatch(entity.getApprovalStatus(),
                    AnnualRebateApprovalEnum.UN_COMMIT, AnnualRebateApprovalEnum.REJECTED, AnnualRebateApprovalEnum.CANCEL)
                    ,McnErrorCode.DISCOUNT_APPROVA_UPDATE_ERROR);
            annualRebateApprovalMapper.updateById(annualRebateApproval);
            annualRebateApproval.setRebateContractCode(entity.getRebateContractCode());
            if(CollectionUtils.isNotEmpty(addDTO.getExcuteIds())) {
                //关联执行单
                List<BillAssociationDetail> associationList = billAssociationDetailMapper.selectList(Wrappers.lambdaQuery(BillAssociationDetail.class)
                        .eq(BillAssociationDetail::getBillId, entity.getId()) .eq(BillAssociationDetail::getBillType, NumberCst.ZERO)
                        .eq(BillAssociationDetail::getIsInvalid, NumberCst.ZERO).eq(BillAssociationDetail::getIsDelete, NumberCst.ZERO));
                List<Long> existExcutionOrderIds = CommonUtil.defaultList(associationList).stream().map(BillAssociationDetail::getExcutionOrderId).distinct().collect(Collectors.toList());
                List<Long> deleteExcutionOrderIds = existExcutionOrderIds.stream().filter(id -> !addDTO.getExcuteIds().contains(id)).collect(Collectors.toList());
                insertExcutionOrderIds = addDTO.getExcuteIds().stream().filter(id -> !existExcutionOrderIds.contains(id)).collect(Collectors.toList());
                if(CollectionUtils.isNotEmpty(deleteExcutionOrderIds)) {
                    //删除关联执行单
                    billAssociationDetailMapper.updateByNoStrategy(Wrappers.lambdaUpdate(BillAssociationDetail.class)
                            .set(BillAssociationDetail::getIsDelete, NumberCst.ONE)
                            .set(BillAssociationDetail::getUpdateBy, UserVoContextHolder.getUserId())
                            .set(BillAssociationDetail::getUpdateName, UserVoContextHolder.getUserName())
                            .set(BillAssociationDetail::getUpdateDate, new Date())
                            .eq(BillAssociationDetail::getBillId, entity.getId())
                            .eq(BillAssociationDetail::getBillType, NumberCst.ZERO)
                            .in(BillAssociationDetail::getExcutionOrderId, deleteExcutionOrderIds)
                            .eq(BillAssociationDetail::getIsDelete, NumberCst.ZERO)
                    );
                    //执行单-客户返点结算状态置为未结算
                    contractExcutionOrderMapper.updateByNoStrategy(Wrappers.<ContractExcutionOrder>lambdaUpdate()
                            .set(ContractExcutionOrder::getRebateSettleStatus, NumberCst.ZERO)
                            .set(ContractExcutionOrder::getUpdateBy, UserVoContextHolder.getUserId())
                            .set(ContractExcutionOrder::getUpdateName, UserVoContextHolder.getUserName())
                            .set(ContractExcutionOrder::getUpdateDate, new Date())
                            .in(ContractExcutionOrder::getId, deleteExcutionOrderIds));
                }
            }
        }
        //关联执行单
        if(CollectionUtils.isNotEmpty(insertExcutionOrderIds)) {
            List<BillAssociationDetail> associationList = billAssociationDetailMapper.selectList(Wrappers.lambdaQuery(BillAssociationDetail.class)
                    .eq(BillAssociationDetail::getBillType, NumberCst.ZERO).in(BillAssociationDetail::getExcutionOrderId, insertExcutionOrderIds)
                    .eq(BillAssociationDetail::getIsInvalid, NumberCst.ZERO).eq(BillAssociationDetail::getIsDelete, NumberCst.ZERO));
            if(CollectionUtils.isNotEmpty(associationList)) {
                String joinCode = associationList.stream().map(BillAssociationDetail::getExcutionOrderCode).collect(Collectors.joining(SeparatorCst.COMMA));
                throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("【%s】执行单已被关联！", joinCode));
            }
            List<ContractExcutionOrder> excutionOrders = contractExcutionOrderMapper.selectList(Wrappers.lambdaQuery(ContractExcutionOrder.class)
                    .in(ContractExcutionOrder::getId, insertExcutionOrderIds).eq(ContractExcutionOrder::getIsDelete, NumberCst.ZERO));
            BusinessAssert.notEmpty(excutionOrders, McnErrorCode.EXECUTION_NOT_EXISTS);
            List<ContractExcutionOrder> invalidList = excutionOrders.stream().filter(e -> !CommonSettleStatusEnum.isUnsettled(e.getRebateSettleStatus())).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(invalidList)) {
                String joinCode = invalidList.stream().map(ContractExcutionOrder::getExcuteCode).collect(Collectors.joining(SeparatorCst.COMMA));
                throw new BusinessException(McnErrorCode.BUSINESS_ERROR.code(), String.format("【%s】执行单非待结算！", joinCode));
            }
            List<BillAssociationDetail> insertList = excutionOrders.stream().map(e -> {
                BillAssociationDetail detail = new BillAssociationDetail();
                detail.setBillId(annualRebateApproval.getId());
                detail.setBillCode(annualRebateApproval.getRebateContractCode());
                detail.setBillType(NumberCst.ZERO);
                detail.setIncomeContractId(e.getIncomeContractId());
                detail.setIncomeContractCode(e.getIncomeContractCode());
                detail.setExcutionOrderId(e.getId());
                detail.setExcutionOrderCode(e.getExcuteCode());
                detail.setIsInvalid(NumberCst.ZERO);
                return detail;
            }).collect(Collectors.toList());
            billAssociationDetailMapper.insertBatch(insertList);
            //执行单-客户返点结算状态置为结算中
            contractExcutionOrderMapper.updateByNoStrategy(Wrappers.<ContractExcutionOrder>lambdaUpdate()
                    .set(ContractExcutionOrder::getRebateSettleStatus, NumberCst.ONE)
                    .set(ContractExcutionOrder::getUpdateBy, UserVoContextHolder.getUserId())
                    .set(ContractExcutionOrder::getUpdateName, UserVoContextHolder.getUserName())
                    .set(ContractExcutionOrder::getUpdateDate, new Date())
                    .in(ContractExcutionOrder::getId, insertExcutionOrderIds));
        }
        //提起审批
        if (AnnualRebateApprovalEnum.isApproval(annualRebateApproval.getApprovalStatus())) {
            approvalFlowService.startProcess(annualRebateApproval.getId(), annualRebateApproval.getRebateContractCode(), ApprovalEnum.ANNUAL_REBATE_APPROVAL,
                    JSON.toJSONString(annualRebateApproval), jsonString(annualRebateApproval));
        }
        return annualRebateApproval.getId();
    }

    /**
     * 详情
     *
     * @param id {@link Long}
     * @return {@link AnnualRebateInfo}
     */
    @Override
    public AnnualRebateInfo info(Long id) {
        AnnualRebateInfo annualRebateInfo = annualRebateApprovalMapper.info(id, ApprovalEnum.ANNUAL_REBATE_APPROVAL.getProcessId());
        BaseCustomerAccountVO baseCustomerAccountVO = Objects.requireNonNull(baseInfoClient.getCustomerAccountById(annualRebateInfo.getPayeeAccountId())).getObj();
        annualRebateInfo.setBaseCustomerAccount(baseCustomerAccountVO);
        if (AnnualTypeEnum.ANNUAL_REBATE.code() == annualRebateInfo.getAnnualType()) {
            YearFrameContract yearFrameContract = yearFrameContractMapper.selectByDiscountCode(annualRebateInfo.getDiscountCode());
            if (Objects.nonNull(yearFrameContract)) {
                annualRebateInfo.setYearContractCode(yearFrameContract.getYearFrameCode());
            }
            DiscountApproval discountApproval = discountApprovalMapper.selectByDiscountCode(annualRebateInfo.getDiscountCode());
            if (Objects.nonNull(discountApproval)) {
                annualRebateInfo.setStartDate(discountApproval.getStartDate());
                annualRebateInfo.setEndDate(discountApproval.getEndDate());
                annualRebateInfo.setDiscountType(discountApproval.getDiscountType());
            }
            PlatformDiscountRangeVO preferentialGradient = platformDiscountRangeMapper.getPreferentialGradient(discountApproval.getId(), annualRebateInfo.getAccumulatedIncomeAmount());
            List<PlatformDiscountRangeVO> rangeVOList = platformDiscountRangeMapper.getByDiscountId(discountApproval.getId());
            rangeVOList.forEach(x -> {
                if (x.equals(preferentialGradient)) {
                    x.setIdentification(true);
                }
            });
            annualRebateInfo.setRangeList(rangeVOList);
        }
        List<BillAssociationDetail> associationList = billAssociationDetailMapper.selectList(Wrappers.lambdaQuery(BillAssociationDetail.class)
                .in(BillAssociationDetail::getBillId, id).in(BillAssociationDetail::getBillType, NumberCst.ZERO)
                .eq(!AnnualRebateApprovalEnum.isInvalid(annualRebateInfo.getApprovalStatus()), BillAssociationDetail::getIsInvalid, NumberCst.ZERO)
                .eq(BillAssociationDetail::getIsDelete, NumberCst.ZERO));
        if(CollectionUtils.isNotEmpty(associationList)) {
            List<Long> excutionOrderIds = associationList.stream().map(BillAssociationDetail::getExcutionOrderId).distinct().collect(Collectors.toList());
            List<AnnualRebateExcutionOrderVO> excutionOrderList = contractExcutionOrderMapper.getAnnualRebateList(excutionOrderIds);
            if(CollectionUtils.isNotEmpty(excutionOrderList)) {
                List<Long> incomeContractIds = excutionOrderList.stream().map(AnnualRebateExcutionOrderVO::getIncomeContractId).distinct().collect(Collectors.toList());
                Map<Long, Integer> incomeContractTypeMap = CommonUtil.defaultList(incomeContractMapper.selectBatchIds(incomeContractIds)).stream().collect(Collectors.toMap(k -> k.getId(), v -> v.getContractType()));
                List<String> brandCodes = excutionOrderList.stream().map(AnnualRebateExcutionOrderVO::getBrandName).filter(Objects::nonNull).distinct().collect(Collectors.toList());
                Map<String,String> brandMap = brandInfoService.getBrandMapByCodes(brandCodes);
                excutionOrderList.forEach(e -> {
                    e.setIncomeContractType(incomeContractTypeMap.get(e.getIncomeContractId()));
                    e.setIncomeContractTypeName(IncomeContractTypeEnum.getContractTypeName(e.getIncomeContractType()));
                    if(brandMap.containsKey(e.getBrandName())) {
                        e.setBrandName(brandMap.get(e.getBrandName()));
                    }
                    e.setApprovalStatusName(ExcutionApprovalStatusEnum.getStatusName(e.getApprovalStatus()));
                    e.setRebateSettleStatusName(CommonSettleStatusEnum.getStatusName(e.getRebateSettleStatus()));
                    e.setPaybackStatusName(PaybackStatusEnum.getStatusName(e.getPayBackStatus()));
                });
            }
            List<Long> contractingIds = CommonUtil.defaultList(contractExcutionOrderMapper.getContractingIds(excutionOrderIds));
            List<ScompanyRes> companyResList = privilegeClient.getParList().getObj();
            Map<Long, String> companyMap = companyResList.stream().collect(Collectors.toMap(k -> k.getCompanyId(), v -> v.getParentCompanyName()));
            if(contractingIds.size() == 0) {
                annualRebateInfo.setCompanyContractSubject(companyMap.get(annualRebateInfo.getContractingId()));
            } else if(contractingIds.size() == 1) {
                annualRebateInfo.setContractingId(ObjectUtils.defaultIfNull(annualRebateInfo.getContractingId(), contractingIds.get(0)));
                annualRebateInfo.setCompanyContractSubject(companyMap.get(annualRebateInfo.getContractingId()));
            } else {
                annualRebateInfo.setContractingId(null);
                List<String> companyContractSubjectList = contractingIds.stream().filter(contractingId -> companyMap.containsKey(contractingId))
                        .map(contractingId -> companyMap.get(contractingId)).collect(Collectors.toList());
                annualRebateInfo.setCompanyContractSubject(StringUtils.join(companyContractSubjectList, SeparatorCst.COMMA));
            }
            annualRebateInfo.setExcutionOrderList(excutionOrderList);

        }
        List<Long> customerIdList = Collections.singletonList(annualRebateInfo.getCustomerId());
        Map<Long, BaseCustomer> customerMapping = Objects.requireNonNull(baseInfoClient.getMcnCustomerByIds(customerIdList)).getObj();
        if (Objects.nonNull(customerMapping)) {
            BaseCustomer customer = customerMapping.get(annualRebateInfo.getCustomerId());
            annualRebateInfo.setCustomerName(Objects.nonNull(customer) ? customer.getCustomerNameC() : "");
        }
        return annualRebateInfo;
    }

    /**
     * 客户返点审批单列表导出
     *
     * @param searchDTO {@link AnnualRebateApprovalSearchDTO}
     * @return {@link Res}
     */
    @Override
    public void exportExcel(AnnualRebateApprovalSearchDTO searchDTO, HttpServletResponse response) {
        searchDTO.setProcessId(ApprovalEnum.ANNUAL_REBATE_APPROVAL.getProcessId());
        searchDTO.setAlias("ara.");
        if (Objects.nonNull(searchDTO.getFlowUserId())) {
            searchDTO.setSrcIds(approvalFlowService.getSrcList(Lists.newArrayList(searchDTO.getFlowUserId()), searchDTO.getProcessId()));
        }
        List<AnnualExportVo> dataList = annualRebateApprovalMapper.exportExcel(searchDTO);
        if (CollectionUtils.isNotEmpty(dataList)) {
            List<String> brandCodes = dataList.stream().map(AnnualExportVo::getBrandName).filter(Objects::nonNull).distinct().collect(Collectors.toList());
            Map<String,String> brandMap = brandInfoService.getBrandMapByCodes(brandCodes);
            List<Long> accountIds = dataList.stream().map(AnnualExportVo::getPayeeAccountId).distinct().collect(Collectors.toList());
            Map<Long, BaseCustomerAccount> customerAccountMap = CommonUtil.defaultList(RpcUtil.getObjException(baseInfoClient.getCustomerAccountByIds(accountIds), "获取客户收款账户信息异常"))
                    .stream().collect(Collectors.toMap(k -> k.getAccountId(), Function.identity()));
            List<Long> customerIdList = dataList.stream().map(AnnualExportVo::getCustomerId).distinct().collect(Collectors.toList());
            Map<Long, BaseCustomer> customerMapping = Objects.requireNonNull(baseInfoClient.getMcnCustomerByIds(customerIdList)).getObj();
            List<Long> flowList = dataList.stream().map(AnnualExportVo::getFlowId).filter(Objects::nonNull).collect(Collectors.toList());
            JSONObject flowMapping = approvalFlowService.getFlowStatus(flowList);
            List<Long> incomeContractIds = dataList.stream().map(AnnualExportVo::getIncomeContractId).distinct().collect(Collectors.toList());
            Map<Long, Integer> incomeContractTypeMap = CommonUtil.getIfNecessary(CollectionUtils.isNotEmpty(incomeContractIds),
                    () -> incomeContractMapper.selectBatchIds(incomeContractIds).stream().collect(Collectors.toMap(k -> k.getId(), v -> v.getContractType())), Collections.EMPTY_MAP);
            dataList.forEach(e -> {
                if(brandMap.containsKey(e.getBrandName())) {
                    e.setBrandName(brandMap.get(e.getBrandName()));
                }
                BaseCustomerAccount baseCustomerAccount = customerAccountMap.get(e.getPayeeAccountId());
                if(Objects.nonNull(baseCustomerAccount)) {
                    e.setAccountName(baseCustomerAccount.getAccountName());
                    e.setType(baseCustomerAccount.getType());
                    e.setCardNumber(baseCustomerAccount.getCardNumber());
                    e.setBankName(baseCustomerAccount.getBankName());
                    e.setBankProvince(baseCustomerAccount.getBankProvince());
                    e.setBankCity(baseCustomerAccount.getBankCity());
                    e.setBankNameBranch(baseCustomerAccount.getBankNameBranch());
                    e.setIdNumber(baseCustomerAccount.getIdNumber());
                    e.setPhoneNumber(baseCustomerAccount.getPhoneNumber());
                }
                if (MapUtils.isNotEmpty(customerMapping)) {
                    BaseCustomer customer = customerMapping.get(e.getCustomerId());
                    e.setCustomerName(Objects.nonNull(customer) ? customer.getCustomerNameC() : "");
                }
                e.setIncomeContractType(incomeContractTypeMap.get(e.getIncomeContractId()));
                e.setIncomeContractTypeName(IncomeContractTypeEnum.getContractTypeName(e.getIncomeContractType()));
                e.setApprovalStatusName(AnnualRebateApprovalEnum.getStatusName(e.getApprovalStatus()));
                e.setPaybackStatusName(PaybackStatusEnum.getStatusName(e.getPaybackStatus()));
                e.setZxdApprovalStatusName(ExcutionApprovalStatusEnum.getStatusName(e.getZxdApprovalStatus()));
                e.setFlowStatusName(CommonUtil.defaultString(flowMapping, e.getFlowId()));

            });
        }
        ExcelUtils.exportExcel(CommonUtil.defaultList(dataList), "客户返点审批单", AnnualExportVo.class, "客户返点审批单.xlsx", response);
    }

    /**
     * 计算金额
     *
     * @param discountCode {@link String}
     * @return {@link AnnualCalculateAmount}
     */
    @Override
    public AnnualCalculateAmount calculateAmount(String discountCode) {
        AnnualCalculateAmount annualCalculateAmount = new AnnualCalculateAmount();
        DiscountApproval discountApproval = discountApprovalMapper.selectByDiscountCode(discountCode);
        //累计收入合同金额
        BigDecimal accumulatedAmount = accumulatedAmount(discountApproval);
        if (Objects.isNull(accumulatedAmount)) {
            accumulatedAmount = BigDecimal.ZERO;
        }
        annualCalculateAmount.setAccumulatedRevenueContractAmount(accumulatedAmount);
        //返点比例
        BigDecimal rebateRatio = platformDiscountRangeService.getRebateRatio(discountApproval.getId(), accumulatedAmount);
        if (null == rebateRatio) {
            rebateRatio = BigDecimal.ZERO;
        }
        annualCalculateAmount.setRebateRatio(rebateRatio);
        BigDecimal rebateAmount = accumulatedAmount.multiply(rebateRatio.divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP)).setScale(2, BigDecimal.ROUND_HALF_UP);
        annualCalculateAmount.setRebateAmount(rebateAmount);
        return annualCalculateAmount;
    }

    /**
     * 财务修改客户返点审批单状态
     *
     * @param statusCallback      {@link ChangeAnnualRebateStatusCallback}
     * @param userVO              {@link UserVO}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void financeStatusChange(ChangeAnnualRebateStatusCallback statusCallback, UserVO userVO) {
        String messageBody = JSONObject.toJSONString(statusCallback);
        MessageInfo messageInfo = MessageInfo.builder()
                .messageTopic(MessageTopicEnum.CHANGE_STATUS_CALLBACK_FD.name())
                .messageKey(CommonUtil.getMessageKey(null))
                .messageBody(messageBody)
                .signature(CommonUtil.hmacMd5Hex(messageBody))
                .status(MessageStatusEnum.SUCCESS.getType())
                .tenantId(Optional.ofNullable(statusCallback.getTenantId()).orElse(UserVoContextHolder.getTenantId()))
                .build();
        messageInfoService.addMessageInfo(messageInfo);
        List<AnnualRebateApproval> dataList = annualRebateApprovalMapper.selectList(Wrappers.lambdaQuery(AnnualRebateApproval.class)
            .in(AnnualRebateApproval::getRebateContractCode, statusCallback.getRebateContractCodes())
            .eq(AnnualRebateApproval::getIsDelete, NumberCst.ZERO));
        if(CollectionUtils.isNotEmpty(dataList)) {
            Set<String> codeSet = dataList.stream().map(AnnualRebateApproval::getRebateContractCode).collect(Collectors.toSet());
            statusCallback.getRebateContractCodes().forEach(code -> {
                BusinessAssert.isTrue(codeSet.contains(code), McnErrorCode.ANNUAL_NOT_EXISTS, "财务-修改客户返点审批单状态回调，查询不到客户返点审批单="+ code);
            });
            List<Long> ids = dataList.stream().map(AnnualRebateApproval::getId).collect(Collectors.toList());
            annualRebateApprovalMapper.updateByNoStrategy(Wrappers.lambdaUpdate(AnnualRebateApproval.class)
                    .set(AnnualRebateApproval::getApprovalStatus, statusCallback.getStatus())
                    .set(AnnualRebateApproval::getUpdateBy, statusCallback.getOperatorId())
                    .set(AnnualRebateApproval::getUpdateName, statusCallback.getOperatorName())
                    .set(AnnualRebateApproval::getUpdateDate, new Date())
                    .in(AnnualRebateApproval::getId, ids));
            //如果更新为已打款
            if(AnnualRebateApprovalEnum.PAID.code() == statusCallback.getStatus().intValue()) {
                //关联执行单
                List<BillAssociationDetail> associationList = billAssociationDetailMapper.selectList(Wrappers.lambdaQuery(BillAssociationDetail.class)
                        .in(BillAssociationDetail::getBillId, ids) .eq(BillAssociationDetail::getBillType, NumberCst.ZERO)
                        .eq(BillAssociationDetail::getIsInvalid, NumberCst.ZERO).eq(BillAssociationDetail::getIsDelete, NumberCst.ZERO));
                if(CollectionUtils.isNotEmpty(associationList)) {
                    List<Long> excutionOrderIds = CommonUtil.defaultList(associationList).stream().map(BillAssociationDetail::getExcutionOrderId).distinct().collect(Collectors.toList());
                    //执行单-客户返点结算状态置为未结算
                    contractExcutionOrderMapper.updateByNoStrategy(Wrappers.<ContractExcutionOrder>lambdaUpdate()
                            .set(ContractExcutionOrder::getRebateSettleStatus, NumberCst.TWO)
                            .set(ContractExcutionOrder::getUpdateBy, UserVoContextHolder.getUserId())
                            .set(ContractExcutionOrder::getUpdateName, UserVoContextHolder.getUserName())
                            .set(ContractExcutionOrder::getUpdateDate, new Date())
                            .in(ContractExcutionOrder::getId, excutionOrderIds));
                }
            }
        }
    }


    /**
     * 根据折扣返点审批单号获取年度优惠政策信息
     *
     * @param discountCode {@link String}
     * @return {@link AnnualPreferentialPolicyInformation}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AnnualPreferentialPolicyInformation getAnnualPreferentialPolicyInformation(String discountCode, BigDecimal accumulatedIncomeAmount) {
        AnnualPreferentialPolicyInformation information = new AnnualPreferentialPolicyInformation();
        YearFrameContract yearFrameContract = yearFrameContractMapper.selectByDiscountCode(discountCode);
        if (Objects.nonNull(yearFrameContract)) {
            information.setYearContractCode(yearFrameContract.getYearFrameCode());
        }
        DiscountApproval discountApproval = discountApprovalMapper.selectByDiscountCode(discountCode);
        if (Objects.nonNull(discountApproval)) {
            information.setStartDate(discountApproval.getStartDate());
            information.setEndDate(discountApproval.getEndDate());
            information.setDiscountType(discountApproval.getDiscountType());
        }
        assert discountApproval != null;
        PlatformDiscountRangeVO preferentialGradient = platformDiscountRangeMapper.getPreferentialGradient(discountApproval.getId(), accumulatedIncomeAmount);
        List<PlatformDiscountRangeVO> rangeVOList = platformDiscountRangeMapper.getByDiscountId(discountApproval.getId());
        rangeVOList.forEach(x -> {
            if (x.equals(preferentialGradient)) {
                x.setIdentification(true);
            }
        });
        information.setRangeList(rangeVOList);
        return information;
    }

    @Override
    public AnnualRebateApproval getByCode(String rebateContractCode) {
        return annualRebateApprovalMapper.selectOne(Wrappers.lambdaQuery(AnnualRebateApproval.class)
                .eq(AnnualRebateApproval::getRebateContractCode, rebateContractCode)
                .eq(AnnualRebateApproval::getIsDelete, NumberCst.ZERO)

        );
    }

    @Override
    public List<AnnualRebateExcutionOrderVO> exportDetail(Long id) {
        AnnualRebateApproval annualRebateApproval = annualRebateApprovalMapper.selectOne(Wrappers.lambdaQuery(AnnualRebateApproval.class)
                        .eq(AnnualRebateApproval::getId, id)
                        .eq(AnnualRebateApproval::getIsDelete, NumberCst.ZERO));
        BusinessAssert.notNull(annualRebateApproval, McnErrorCode.ANNUAL_NOT_EXISTS);
        List<BillAssociationDetail> associationList = billAssociationDetailMapper.selectList(Wrappers.lambdaQuery(BillAssociationDetail.class)
                .in(BillAssociationDetail::getBillId, id).in(BillAssociationDetail::getBillType, NumberCst.ZERO)
                .eq(!AnnualRebateApprovalEnum.isInvalid(annualRebateApproval.getApprovalStatus()), BillAssociationDetail::getIsInvalid, NumberCst.ZERO)
                .eq(BillAssociationDetail::getIsDelete, NumberCst.ZERO));
        List<AnnualRebateExcutionOrderVO> excutionOrderList = null;
        if(CollectionUtils.isNotEmpty(associationList)) {
            List<Long> excutionOrderIds = associationList.stream().map(BillAssociationDetail::getExcutionOrderId).distinct().collect(Collectors.toList());
            excutionOrderList = contractExcutionOrderMapper.getAnnualRebateList(excutionOrderIds);
            if(CollectionUtils.isNotEmpty(excutionOrderList)) {
                List<Long> incomeContractIds = excutionOrderList.stream().map(AnnualRebateExcutionOrderVO::getIncomeContractId).distinct().collect(Collectors.toList());
                Map<Long, Integer> incomeContractTypeMap = CommonUtil.defaultList(incomeContractMapper.selectBatchIds(incomeContractIds)).stream().collect(Collectors.toMap(k -> k.getId(), v -> v.getContractType()));
                List<String> brandCodes = excutionOrderList.stream().map(AnnualRebateExcutionOrderVO::getBrandName).filter(Objects::nonNull).distinct().collect(Collectors.toList());
                Map<String,String> brandMap = brandInfoService.getBrandMapByCodes(brandCodes);
                excutionOrderList.forEach(e -> {
                    e.setIncomeContractType(incomeContractTypeMap.get(e.getIncomeContractId()));
                    e.setIncomeContractTypeName(IncomeContractTypeEnum.getContractTypeName(e.getIncomeContractType()));
                    if(brandMap.containsKey(e.getBrandName())) {
                        e.setBrandName(brandMap.get(e.getBrandName()));
                    }
                    e.setApprovalStatusName(ExcutionApprovalStatusEnum.getStatusName(e.getApprovalStatus()));
                    e.setRebateSettleStatusName(CommonSettleStatusEnum.getStatusName(e.getRebateSettleStatus()));
                    e.setPaybackStatusName(PaybackStatusEnum.getStatusName(e.getPayBackStatus()));
                    e.setAnnualType(annualRebateApproval.getAnnualType());
                    e.setRebateContractCode(annualRebateApproval.getRebateContractCode());
                });
            }
        }
        return excutionOrderList;
    }


    /**
     * 计算累计收入合同金额
     *
     * @param discountApproval {@link DiscountApproval}
     * @return {@link BigDecimal}
     */
    @SneakyThrows
    public BigDecimal accumulatedAmount(DiscountApproval discountApproval) {
        BigDecimal accumulatedAmount = new BigDecimal(0);
        DiscountApprovalEnum discountTypeEnum = DiscountApprovalEnum.getByCode("discountType", discountApproval.getDiscountType());
        Date startTime = DateUtils.parseDate(discountApproval.getStartDate(), "yyyy-MM-dd HH:mm:ss");
        switch (Objects.requireNonNull(discountTypeEnum)) {
            //年度优惠含平台返点
            case YEAR_WITH_PLATFORM_DISCOUNT:
                accumulatedAmount = incomeContractMapper.getAnnualIncludingPlatformRebate(discountApproval.getId(), startTime, new Date());
                break;
            case YEAR_NOT_WITH_PLATFORM_DISCOUNT:
                accumulatedAmount = incomeContractMapper.getAnnualNotIncludePlatformRebate(discountApproval.getId(), startTime, new Date());
                break;
            case YEAR_TYPE_DISCOUNT:
                accumulatedAmount = incomeContractMapper.getAnnualAmount(discountApproval.getId(), startTime, new Date());
                break;
            default:
        }
        return accumulatedAmount;
    }


    /**
     * 判断审批状态
     *
     * @param annualRebateApproval {@link AnnualRebateApproval}
     * @param status               {@link Integer}
     */
    @SuppressWarnings("AlibabaAvoidComplexCondition")
    private void validateStatus(AnnualRebateApproval annualRebateApproval, Integer status) {
        if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.UN_COMMIT.code() &&
                (status != AnnualRebateApprovalEnum.APPROVAL.code() && status != AnnualRebateApprovalEnum.INVALID.code())) {
            throw new BusinessException(McnErrorCode.ANNUAL_REBATE_UN_COMMIT_ERROR);
        }
        if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.APPROVAL.code() &&
                (status != AnnualRebateApprovalEnum.CANCEL.code() && status != AnnualRebateApprovalEnum.INVALID.code())) {
            throw new BusinessException(McnErrorCode.ANNUAL_REBATE_APPROVAL_ERROR);
        }
        if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.WAITING_PAYMENT.code() &&
                (status != AnnualRebateApprovalEnum.INVALID.code())) {
            throw new BusinessException(McnErrorCode.ANNUAL_REBATE_WAITING_PAYMENT_ERROR);
        }
        if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.REJECTED.code() &&
                (status != AnnualRebateApprovalEnum.APPROVAL.code() && status != AnnualRebateApprovalEnum.INVALID.code())) {
            throw new BusinessException(McnErrorCode.ANNUAL_REBATE_REJECTED_ERROR);
        }
        if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.CANCEL.code() &&
                (status != AnnualRebateApprovalEnum.APPROVAL.code() && status != AnnualRebateApprovalEnum.INVALID.code())) {
            throw new BusinessException(McnErrorCode.ANNUAL_REBATE_CANCEL_ERROR);
        }
        if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.PAID.code()) {
            throw new BusinessException(McnErrorCode.ANNUAL_REBATE_PAID_ERROR);
        }

        if (annualRebateApproval.getApprovalStatus().equals(AnnualRebateApprovalEnum.INVALID.code())) {
            throw new BusinessException(McnErrorCode.ANNUAL_INVALID);
        }
        if (status.equals(AnnualRebateApprovalEnum.WAITING_PAYMENT.code()) || status.equals(AnnualRebateApprovalEnum.REJECTED.code())) {
            if (annualRebateApproval.getApprovalStatus().equals(AnnualRebateApprovalEnum.CANCEL.code())) {
                throw new BusinessException(McnErrorCode.ANNUAL_CANCELED);
            }
        }
    }

    /**
     * 审批回调
     *
     * @param approvalFlowDTO 流程对对象
     * @param status          最后节点的同意 false 拒绝
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approvalCallback(ApprovalFlowDTO approvalFlowDTO, Boolean status) {
        AnnualRebateApproval annualRebateApproval = annualRebateApprovalMapper.selectById(approvalFlowDTO.getSrcId());
        approvalFlowDTO.setSrcCode(annualRebateApproval.getRebateContractCode());
        approvalFlowDTO.addSubmitInfoIfNull(annualRebateApproval);
        if (Objects.isNull(status)) {
            //审批流配置-接入待办
            return;
        }
        annualRebateApproval.setApprovalStatus(status ? AnnualRebateApprovalEnum.WAITING_PAYMENT.code() : AnnualRebateApprovalEnum.REJECTED.code());
        annualRebateApproval.setUpdateBy(approvalFlowDTO.getUserId());
        annualRebateApproval.setUpdateName(approvalFlowDTO.getUserName());
        annualRebateApproval.setUpdateDate(new Date());
        annualRebateApprovalMapper.updateById(annualRebateApproval);
        approvalFlowService.sendNotify(approvalFlowDTO, annualRebateApproval.getId(), annualRebateApproval.getRebateContractCode()
                , ApprovalEnum.ANNUAL_REBATE_APPROVAL, status, annualRebateApproval.getCreateBy(), annualRebateApproval.getCreateName());

        if (annualRebateApproval.getApprovalStatus() == AnnualRebateApprovalEnum.WAITING_PAYMENT.code()) {
            annualRebateApprovalProducer.sendRecordToMq(annualRebateApproval.getId());
        }
    }
}
